package com.wave.olap;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.annotation.PostConstruct;

import oracle.olapi.data.cursor.CompoundCursor;
import oracle.olapi.data.cursor.Cursor;
import oracle.olapi.data.cursor.ValueCursor;
import oracle.olapi.data.source.CursorManagerSpecification;
import oracle.olapi.data.source.Source;
import oracle.olapi.data.source.SpecifiedCursorManager;
import oracle.olapi.data.source.StringSource;
import oracle.olapi.examples.BaseExample;
import oracle.olapi.examples.Context10g;
import oracle.olapi.examples.CursorPrintWriter;
import oracle.olapi.examples.Logger;
import oracle.olapi.metadata.mdm.MdmDimension;
import oracle.olapi.metadata.mdm.MdmHierarchy;
import oracle.olapi.metadata.mdm.MdmLevelHierarchy;
import oracle.olapi.metadata.mdm.MdmMeasure;
import oracle.olapi.metadata.mdm.MdmObject;
import oracle.olapi.metadata.mdm.MdmPrimaryDimension;
import oracle.olapi.transaction.NotCommittableException;
import oracle.olapi.transaction.TransactionInactiveException;

public class OracleOlapService extends BaseExample {
	
	protected CursorPrintWriter cpw;
    protected Logger logger;
	protected Context10g cxt;
	
	protected static org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger("com.wave.olap.OracleOlapService");
	
	private String url;
	private String user;
	private String password;
	
	public OracleOlapService() {
	}
	
	@PostConstruct
	public void postConstruct() {
		Properties p = new Properties();
		p.put("url", getUrl());
		p.put("user", getUser());
		p.put("password", getPassword());
		
		if (this.getUrl() == null || this.getUrl().equals("") || 
				this.getUser() == null || this.getUser().equals("")) {
			log4jLogger.error("Could Not Connect to Database correctly.  Please correct the jdbc.properties file");
			return;
		}
		
        try {
        	initialize(p);
        } catch (Exception e) { 
        	log4jLogger.error("Could Not Connect to Database correctly.  Please correct the jdbc.properties file");
        }
        cxt = new Context10g(p, getLogger());
	}
	
	
	public OlapBean<String, ?> query(OlapCriteria<String> oc) {
		MdmMeasure m = cxt.getMdmMeasureByName(oc.measures.get(0));
		//Source sm = m.getSource();
		List<MdmDimension> dl = m.getDimensions();
		List<String> fullOrder = new ArrayList<String>();
		for  (String o : oc.getOrder()) {
			fullOrder.add(o);
		}
		for (MdmDimension d : dl) {
			if (!fullOrder.contains(d.getName()))
				fullOrder.add(d.getName());
		}
		List<MdmObject> dimension = new ArrayList<MdmObject>();
		List<Source> dimDataSourceList = new ArrayList<Source>();
		List<Source> ls = new ArrayList<Source>();
 		for (String name : fullOrder) {
			MdmPrimaryDimension mdmPd = cxt.getMdmPrimaryDimensionByName(name);
			MdmHierarchy mdmH =  mdmPd.getDefaultHierarchy();
			//MdmLevelHierarchy mdmLh = (MdmLevelHierarchy) mdmPd.getDefaultHierarchy();
			dimension.add(mdmPd);
			Source shortValueDesc = mdmPd.getShortValueDescriptionAttribute().getSource();
			//Source valueDesc = mdmPd.getValueDescriptionAttribute().getSource();
			List<String> selectCriteria = oc.getCriteria().get(name);
			Source s = null;
			if (selectCriteria != null && selectCriteria.size() > 0) {
				String[] stringCriteria = (String[])selectCriteria.toArray(new String[selectCriteria.size()]);
				s = ((StringSource)mdmH.getSource()).selectValues( stringCriteria );
			}
			else
				s = mdmPd.getSource();
			dimDataSourceList.add(shortValueDesc.join(s));
			ls.add(s);
 		}
 		//prepareAndCommit();
 		Source ms = m.getSource();
 		for (Source s : ls) {
 			ms = ms.join(s);
 		}
 		return createBean(ms, dimension, getDimensionDataElements(dimDataSourceList));
	}
	
	public OlapBean<String,?> createBean(Source s, List<MdmObject> dimension, List<List<OlapTableObject> > dimensionData) {
		prepareAndCommit();
		CursorManagerSpecification cursorMngrSpec = cxt.getDataProvider().createCursorManagerSpecification(s);
		SpecifiedCursorManager cursorMngr = cxt.getDataProvider().createCursorManager(cursorMngrSpec);
		CompoundCursor unitsForSelCursor = (CompoundCursor)cursorMngr.createCursor();
	    ValueCursor specifiedUnitsVals = unitsForSelCursor.getValueCursor();
	    List<ValueCursor> outputs = unitsForSelCursor.getOutputs();
	    int size = outputs.size();
	    
	    OlapBean<String,Object> bean = new OracleOlapBean<Object>();
	    do {
	        List<String> keys = new ArrayList<String>();
	        for (int j = size-1; j >= 0; j--) 
	        	keys.add( outputs.get(j).getCurrentString() );
	        MultiDimensionalKey<String> key = new MultiDimensionalKey<String>(keys);
	        bean.add(key, specifiedUnitsVals.getCurrentValue());
	    } while(unitsForSelCursor.next());
	    cursorMngr.close();
	    bean.setDimensionMetadata(dimension, dimensionData);
	    return bean;
	}
	
	public List<OlapTableObject> getDimensionDataElements(String dimension) {
		MdmPrimaryDimension mdmPd = cxt.getMdmPrimaryDimensionByName(dimension);
		//MdmHierarchy mdmH =  mdmPd.getDefaultHierarchy();
		//MdmLevelHierarchy mdmLh = (MdmLevelHierarchy) mdmPd.getDefaultHierarchy();
		Source shortValueDesc = mdmPd.getShortValueDescriptionAttribute().getSource();
		return getDimensionDataElements(shortValueDesc.join(mdmPd.getSource()));
	}
	
	protected List<List<OlapTableObject>> getDimensionDataElements(List<Source> dimDataSourceList) {
		List<List<OlapTableObject>> list = new ArrayList< List<OlapTableObject> > ();
		for (Source s : dimDataSourceList) {
			list.add(getDimensionDataElements(s));
		}
		return list;
	}
	
	protected List<OlapTableObject> getDimensionDataElements(Source s) {
		prepareAndCommit();
		CursorManagerSpecification cursorMngrSpec = cxt.getDataProvider().createCursorManagerSpecification(s);
		SpecifiedCursorManager cursorMngr = cxt.getDataProvider().createCursorManager(cursorMngrSpec);
		CompoundCursor cc = (CompoundCursor)cursorMngr.createCursor();
		ValueCursor vcc = cc.getValueCursor();
	    List<ValueCursor> outputs = cc.getOutputs();
	    //int size = outputs.size();
	    List<OlapTableObject> list = new ArrayList<OlapTableObject>();
	    for (Cursor cursor : outputs) {
			try {
				CompoundCursor cCursor = (CompoundCursor) cursor;
			} catch (ClassCastException cce) {
				try {
					ValueCursor vc = (ValueCursor) cursor;
					do {
						list.add( new OlapTableObject( (String)vc.getCurrentValue(), (String)vcc.getCurrentValue()));
					} while (vc.next());
				} catch (ClassCastException cce2) {
				}
			}
		}
	    cursorMngr.close();
		return list;
	}
	
	protected void prepareAndCommit() {
 		try {
 			cxt.getTransactionProvider().prepareCurrentTransaction();
 			cxt.getTransactionProvider().commitCurrentTransaction();
 		} catch (NotCommittableException nce) {
 		} catch (TransactionInactiveException tie) { }
	}
	
	public void run() {}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUser() {
		return user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
    
}
